package com.corticon.dialogs.service;

import com.corticon.dialogs.dao.MessageDAO;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.annotation.security.RolesAllowed;
import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.annotations.RemoteProxy;
import org.directwebremoting.annotations.Param;
import org.directwebremoting.spring.SpringCreator;

import com.corticon.dialogs.dao.QuestionDAO;
import com.corticon.dialogs.dao.QuestionInSetDAO;
import com.corticon.dialogs.dao.QuestionSetDAO;
import com.corticon.dialogs.dao.ResponseEntryDAO;
import com.corticon.dialogs.dao.hibernate.QuestionnaireManager;
import com.corticon.dialogs.dto.PreviewQuestionDTO;
import com.corticon.dialogs.dto.QuestionDTO;
import com.corticon.dialogs.model.PermissionType;
import com.corticon.dialogs.model.Question;
import com.corticon.dialogs.model.QuestionInSet;
import com.corticon.dialogs.model.QuestionSet;
import com.corticon.dialogs.model.ResponseEntry;
import com.corticon.dialogs.model.User;
import com.corticon.dialogs.util.CommonSupport;
import com.corticon.dialogs.util.MyComparator;
import com.corticon.dialogs.util.QuestionUtils;
import com.corticon.dialogs.util.SystemMessages;

/**
 * 
 * @author Lifan Yang
 * @since 2008-11-01
 */
@RemoteProxy(name = "QuestionSetService", creator = SpringCreator.class, creatorParams = @Param(name = "beanName", value = "questionSetService"))
@Service("questionSetService")
public class QuestionSetServiceImpl implements QuestionSetService {

	// DAOs.
	@Resource
	private MessageDAO messageDAO;
	@Resource
	private QuestionDAO questionDAO;
	@Resource
	private QuestionSetDAO questionSetDAO;
	@Resource
	private QuestionInSetDAO questionInSetDAO;
	@Resource
	private ResponseEntryDAO responseEntryDAO;
	@Resource
	private CommonSupport commonSupport;

    @Resource
    private QuestionnaireManager questionnaireManager;

	@RolesAllowed(value = { "ROLE_PORTAL_USER", "ROLE_AUTHOR" })
	@Transactional(readOnly = true)
	@RemoteMethod
	@Override
	public List<QuestionDTO> searchUnincludedQuestions(String keywords) {

		User currentUser = commonSupport.findCurrentUser();

		String username = null;
		String group = null;
		if (currentUser.getPermissions().contains(PermissionType.AUTHOR.value())) {
			group = currentUser.getUserGroup().getName();
		} else {
			username = currentUser.getUsername();
		}

		List<Question> questions = new ArrayList<Question>();
		questions = questionDAO.searchUnincludedQuestions(keywords, username, group);

		List<QuestionDTO> results = new ArrayList<QuestionDTO>();
		for (Question question : questions) {
			QuestionDTO qdto = new QuestionDTO(question);
			results.add(qdto);
		}

		return results;
	}

	@Transactional
	@Override
	public QuestionDTO includeQuestion(Long questionId, Integer maxSequence) {
		// The questionIds cannot be empty.
		Assert.notNull(questionId);

		Question question = questionDAO.findById(questionId);

		Integer nextSequence = maxSequence + 1;
		QuestionDTO qdto = new QuestionDTO(question, nextSequence.toString());

		return qdto;
	}

	@RolesAllowed(value = { "ROLE_PORTAL_USER", "ROLE_AUTHOR" })
	@Transactional
	@RemoteMethod
	@Override
	public String remove(Long questionSetId) {
		Assert.notNull(questionSetId);

		QuestionSet questionSet = questionSetDAO.findById(questionSetId);

		if (questionSet == null) {
			return SystemMessages.ACTION_FAILED;
		}

		String actionMessageKey = commonSupport.checkQuestionAvailability(questionSet);
		if (!actionMessageKey.equals(SystemMessages.ACTION_SUCCESS)) {
			return messageDAO.findByKey(actionMessageKey).getValue();
		}

		if (questionSet.getQuesitonSetInNaires().size() > 0) {
			return messageDAO.findByKey(SystemMessages.ACTION_FAILED_PAGE_INCLUDED).getValue();
		}

		// Update all of siblings.
		for (QuestionSet sibling : questionSetDAO.findBySibling(questionSet)) {
			sibling.setSibling(null);
			questionSetDAO.update(sibling);
		}
        questionnaireManager.deletePage(questionSet.getQuestionSetId());
		questionSetDAO.delete(questionSet);

		return SystemMessages.ACTION_SUCCESS;
	}

	@RolesAllowed( { "ROLE_PORTAL_USER", "ROLE_AUTHOR" })
	@Transactional
	@RemoteMethod
	@Override
	public boolean removeQuestionsFromSet(Long qsId, Long[] qisIds) {
		Assert.notEmpty(qisIds);

		for (Long qisId : qisIds) {
			QuestionInSet qis = questionInSetDAO.load(qisId);
            questionnaireManager.deletePageinQuestion(qis.getId());
			questionInSetDAO.delete(qis);
		}

		return true;
	}

	@Transactional
	@Override
	public void saveOrUpdate(QuestionSet questionSet, List<QuestionDTO> questions) {

		User currentUser = commonSupport.findCurrentUser();

		// QuestionSet questionSet = questionSetDAO.findById(questionSetId);

		for (Iterator<QuestionInSet> qisIterator = questionSet.getQuestionsInSet().iterator(); qisIterator.hasNext();) {

			QuestionInSet qis = qisIterator.next();

			// The var is used to mark a QuestionInSet whether should to be
			// removed or not.
			boolean isRemoved = true;
			for (QuestionDTO questionDTO : questions) {

				if (StringUtils.isNotEmpty(questionDTO.getQisId()) && qis.getId().equals(Long.parseLong(questionDTO.getQisId()))) {
					/*
					 * If there is a QuestionInSet involved in the Question List
					 * on the web page, then this QuestionInSet will be updated,
					 * isRemoved flag will be set as flase and the current loop
					 * will be broke.
					 */
					qis.setSequence(questionDTO.getSequence());
					qis.setPriority(QuestionUtils.calculatePriority(questionDTO.getSequence()));
					qis.setModifiedBy(currentUser);
					isRemoved = false;
					break;
				}
			}

			if (isRemoved) {
				qisIterator.remove();
                questionnaireManager.deletePageinQuestion(qis.getId());
				questionInSetDAO.delete(qis);
                //And Delete from q_dialoginstance q_response q_displayflag
			}
		}

		// Add new links between the question set and the question.
		for (QuestionDTO questionDTO : questions) {
			if (StringUtils.isEmpty(questionDTO.getQisId())) {
				// If the qisId of a QuestionDTO is empty, then create a new
				// QuestionInSet.
				Question question = questionDAO.load(questionDTO.getQuestionId());
				Double priority = QuestionUtils.calculatePriority(questionDTO.getSequence());
				QuestionInSet newQis = new QuestionInSet(question, questionSet, questionDTO.getSequence(), priority);
				newQis.setModifiedBy(currentUser);
				questionSet.getQuestionsInSet().add(newQis);
			}
		}

		questionSet.setModifiedBy(currentUser);
		questionSetDAO.saveOrUpdate(questionSet);
	}

	@Override
	public List<PreviewQuestionDTO> getPreviewQuestionDTOList(QuestionSet questionSet) {
		List<PreviewQuestionDTO> dtos = new ArrayList<PreviewQuestionDTO>();

		for (QuestionInSet qis : questionSet.getQuestionsInSet()) {
			Question question = qis.getQuestion();

			PreviewQuestionDTO pqdto = new PreviewQuestionDTO();

			// Set the response type of the question.
			pqdto.setResponseType(question.getResponseType().getName());

			if (question.getResponseType().getUicontrol() != null) {
				pqdto.setUiType(question.getResponseType().getUicontrol().getName());
			}

			// Set other properties from Question to PreviewQuestionDTO.
			pqdto.setSequence(qis.getSequence());
			pqdto.setText(question.getText());
			List<ResponseEntry> oldResponses = responseEntryDAO.findByResponseType(question.getResponseType());
			Object[] allData = oldResponses.toArray();
			if (allData != null) {
				Arrays.sort(allData, new MyComparator());
				List<ResponseEntry> NewResponses = new ArrayList();
				for (int i = 0; i < allData.length; i++) {
					NewResponses.add((ResponseEntry) allData[i]);
				}
				pqdto.setResponses(NewResponses);
			}
			pqdto.setWidth(question.getWidth());

			dtos.add(pqdto);
		}

		return dtos;
	}
}
